package ui;

import java.io.File;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;

import javax.swing.text.ZoneView;

import org.lwjgl.opengl.GL11;

import sim.SimulationParameters;
import world.Ameisenhaufen;
import animal.Spinne;

import com.badlogic.gdx.ApplicationListener;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input;
import com.badlogic.gdx.InputMultiplexer;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.Pixmap;
import com.badlogic.gdx.graphics.Pixmap.Format;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.Texture.TextureFilter;
import com.badlogic.gdx.graphics.g2d.Animation;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.graphics.g2d.Sprite;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer.ShapeType;
import com.badlogic.gdx.maps.MapProperties;
import com.badlogic.gdx.maps.tiled.TiledMap;
import com.badlogic.gdx.maps.tiled.TiledMapTile;
import com.badlogic.gdx.maps.tiled.TiledMapTileSet;
import com.badlogic.gdx.maps.tiled.TiledMapTileSets;
import com.badlogic.gdx.maps.tiled.TmxMapLoader;
import com.badlogic.gdx.maps.tiled.renderers.OrthogonalTiledMapRenderer;
import com.badlogic.gdx.math.Rectangle;
import com.badlogic.gdx.math.Vector3;
import com.badlogic.gdx.scenes.scene2d.Actor;
import com.badlogic.gdx.scenes.scene2d.Event;
import com.badlogic.gdx.scenes.scene2d.EventListener;
import com.badlogic.gdx.scenes.scene2d.Stage;
import com.badlogic.gdx.scenes.scene2d.ui.Container;
import com.badlogic.gdx.scenes.scene2d.ui.Image;
import com.badlogic.gdx.scenes.scene2d.ui.Label;
import com.badlogic.gdx.scenes.scene2d.ui.Skin;
import com.badlogic.gdx.scenes.scene2d.ui.Slider;
import com.badlogic.gdx.scenes.scene2d.ui.Slider.SliderStyle;
import com.badlogic.gdx.scenes.scene2d.ui.Table;
import com.badlogic.gdx.scenes.scene2d.ui.TextButton;
import com.badlogic.gdx.scenes.scene2d.ui.Label.LabelStyle;
import com.badlogic.gdx.scenes.scene2d.ui.TextButton.TextButtonStyle;
import com.badlogic.gdx.scenes.scene2d.ui.TextField;
import com.badlogic.gdx.scenes.scene2d.ui.TextField.TextFieldStyle;
import com.badlogic.gdx.scenes.scene2d.utils.Align;
import com.badlogic.gdx.scenes.scene2d.utils.ChangeListener;
import com.badlogic.gdx.utils.viewport.ScreenViewport;

public class AntSimRenderer implements ApplicationListener {
	private Logger logger = Logger.getLogger(AntSimRenderer.class.getName());
	private SpriteBatch batch;
	private ShapeRenderer shapeRenderer;
	private Sprite backgroundSprite;
	private List<Grafikobjekt> graphicsToRender = Collections
			.synchronizedList(new ArrayList<Grafikobjekt>());
	private Map<String, Texture> textureMap = new HashMap<String, Texture>();
	private BitmapFont font;
	private int windowWidth;
	private Animation walkAnimation;
	private Animation spiderAnimation;
	private int windowHeight;
	private TextureRegion currentFrame;
	private float stateTime;
	private OrthographicCamera cam;
	private Rectangle glViewport;
	private float rotationSpeed;
	private TiledMap worldMap;
	private OrthogonalTiledMapRenderer tileRenderer;
	private double simSpeed;
	private double maxSimSpeed;
	private Rectangle simBorder;
	private SimulationParameters parameters;
	private Stage stage;
	private Skin skin;
	private TextButton button;
	private Table table;
	private Label fpsLabel;
	private Label simSpeedLabel;
	private Table parameterTable;
	public AntSimRenderer(SimulationParameters parameters){
		this.parameters = parameters;
	}

	public void create() {
		loadGraphics();
		font = new BitmapFont();
		font.setScale(2);
		font.setColor(Color.BLACK);
		batch = new SpriteBatch();
		shapeRenderer = new ShapeRenderer();

		antAnim();
		spiderAnim();

		cam = new OrthographicCamera(windowWidth, windowHeight);
		cam.position.set(windowWidth / 2, windowHeight / 2, 0);

		batch.setProjectionMatrix(cam.combined);
		shapeRenderer.setProjectionMatrix(cam.combined);

		glViewport = new Rectangle(0, 0, windowWidth, windowHeight);
		worldMap = new TmxMapLoader().load("tiles/world.tmx");
		System.out.println("world: " + worldMap.getTileSets());

		float unitScale = 1f;
		tileRenderer = new OrthogonalTiledMapRenderer(worldMap, unitScale);
		tileGeomAnalyzer();		
		stage = new Stage(new ScreenViewport());
		createUI();
		InputMultiplexer multiplexer = new InputMultiplexer();
		multiplexer.addProcessor(stage);	
		Gdx.input.setInputProcessor(multiplexer);
	}

	private void loadGraphics(){
		File assetFolder = new File("grafiken");
		for (File assetFile : assetFolder.listFiles()) {
			if (assetFile.getName().endsWith(".png")
					|| assetFile.getName().endsWith(".jpg")
					|| assetFile.getName().endsWith(".gif")) {
				String pathName = assetFile.getPath().replace("\\", "/");				
				textureMap.put(pathName,
						new Texture(Gdx.files.internal(pathName)));

			}
		}
		logger.info("graphics loaded:" + textureMap);

		backgroundSprite = new Sprite(textureMap.get("grafiken/rasen1.jpg"));
		backgroundSprite.scale(4);
	}

	private void createUI(){
		skin = new Skin();
		skin = new Skin(Gdx.files.internal("data/uiskin.json"));
		table = new Table();
	
		table.setFillParent(true);
		stage.addActor(table);
		
		Table statsTabel = new Table();		
		fpsLabel = new Label("FPS: ",skin);
		simSpeedLabel = new Label("SIM: ",skin);
		statsTabel.add(new Label("FPS",skin)).width(100);		
		statsTabel.add(fpsLabel).width(100);			
		statsTabel.row();
		statsTabel.add(new Label("SIM",skin)).width(100);		
		statsTabel.add(simSpeedLabel).width(100);	
		table.add(statsTabel).expandX().padTop(100);

		parameterTable = new Table();		
		parameterTable.add(new Label("Parameters",skin)).colspan(2);
		
		addUIParameter("Move speed spider:", 0,50,1,null);
		addUIParameter("Move speed ant   :", 0,50,1,null);
		addUIParameter("Move speed bug   :", 0,50,1,null);
		
		table.row();
		table.add(parameterTable).padRight(100);		
	}

	private void addUIParameter(String name,float min, float max,float step, Field parameter){
		parameterTable.row();
		parameterTable.add(new Label(name,skin)).width(200);

		final Slider slider = new Slider(min,max,step, false,skin);
		parameterTable.add(slider).width(80);
		
		slider.addListener(new ChangeListener() {
			public void changed (ChangeEvent event, Actor actor) {
				Gdx.app.log("UITest", "slider: " + slider.getValue());
			}
		});
	}	

	public void showBorder(int x,int y,int width,int height){
		simBorder = new Rectangle(x,y,width,height);
	}

	private void spiderAnim() {
		int FRAME_COLS = 2;
		int FRAME_ROWS = 1;
		TextureRegion[] walkFrames;
		Texture walkSheet = new Texture(
				Gdx.files.internal("grafiken/spiderAnim2.png"));
		TextureRegion[][] tmp = TextureRegion.split(walkSheet,
				walkSheet.getWidth() / FRAME_COLS, walkSheet.getHeight()
				/ FRAME_ROWS);
		walkFrames = new TextureRegion[FRAME_COLS * FRAME_ROWS];
		int index = 0;
		for (int i = 0; i < FRAME_ROWS; i++) {
			for (int j = 0; j < FRAME_COLS; j++) {
				walkFrames[index++] = tmp[i][j];
			}
		}
		spiderAnimation = new Animation(0.2f, walkFrames);
	}

	private void tileGeomAnalyzer() {
		MapProperties props = worldMap.getProperties();
		Iterator<String> keyIter = props.getKeys();
		while (keyIter.hasNext()) {
			String key = keyIter.next();
			System.out.println(key + ":" + props.get(key));
		}
		TiledMapTileSets sets = worldMap.getTileSets();
		Iterator<TiledMapTileSet> tileSetIter = sets.iterator();
		while (tileSetIter.hasNext()) {
			TiledMapTileSet tileSet = tileSetIter.next();
			System.out.println(tileSet.getName());
			if ("Desert".equals(tileSet.getName())) {
				System.out.println(tileSet.getProperties());
				Iterator<TiledMapTile> mapTileIter = tileSet.iterator();
				while (mapTileIter.hasNext()) {
					TiledMapTile tile = mapTileIter.next();
					System.out.println(tile);
					MapProperties tileProps = tile.getProperties();
					Iterator<String> tileKeyIter = tileProps.getKeys();
					while (tileKeyIter.hasNext()) {
						String key = tileKeyIter.next();
						System.out.println(key + ":" + tileProps.get(key));
					}
				}
			}
		}
	}

	private void antAnim() {
		int FRAME_COLS = 4;
		int FRAME_ROWS = 2;
		TextureRegion[] walkFrames;
		Texture walkSheet = new Texture(
				Gdx.files.internal("grafiken/antspriteanim.png"));
		TextureRegion[][] tmp = TextureRegion.split(walkSheet,
				walkSheet.getWidth() / FRAME_COLS, walkSheet.getHeight()
				/ FRAME_ROWS);
		walkFrames = new TextureRegion[FRAME_COLS * FRAME_ROWS];
		int index = 0;
		for (int i = 0; i < FRAME_ROWS; i++) {
			for (int j = 0; j < FRAME_COLS; j++) {
				walkFrames[index++] = tmp[i][j];
			}
		}

		walkAnimation = new Animation(0.025f, walkFrames);

	}

	public void addObjectToRender(Grafikobjekt graphicsObj) {
		graphicsToRender.add(graphicsObj);
	}

	public void removeObjectFromRender(Grafikobjekt graphicsObj) {
		graphicsToRender.remove(graphicsObj);
	}

	public void render() {

		handleInput();
		cam.update();
		Gdx.graphics.getGL20().glClearColor(1f, 1f, 1f, 1);
		Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
		batch.setProjectionMatrix(cam.combined);

		batch.begin();
		backgroundSprite.setPosition((parameters.getWorldWidth()-backgroundSprite.getWidth())/2,0);
		backgroundSprite.draw(batch);
		batch.end();

		tileRenderer.setView(cam);
		// tileRenderer.render();

		batch.begin();

		stateTime += Gdx.graphics.getDeltaTime();
		currentFrame = spiderAnimation.getKeyFrame(stateTime, true);
		synchronized (graphicsToRender) {
			Iterator<Grafikobjekt> i = graphicsToRender.iterator();
			while (i.hasNext()) {
				Grafikobjekt gobj = i.next();
				Texture texToUse = textureMap.get(gobj.getGrafik());
				float scale = 1;
				if (texToUse != null) {
					float rotation = (float) Math.toDegrees(gobj.getDrehung());
					if (gobj instanceof Ameisenhaufen) {
						scale = 3;
					}
					if (gobj instanceof Spinne) {
						batch.draw(
								currentFrame,
								// position
								(int) (gobj.getX() - texToUse.getWidth() / 2),
								(int) (parameters.getWorldHeight() - gobj.getY() - texToUse
										.getHeight() / 2),
										// origin
										(int) texToUse.getWidth() / 2, (int) texToUse
										.getHeight() / 2,
										// width,height
										texToUse.getWidth(), texToUse.getHeight(),
										// scale
										scale, scale,
										// rotation
										rotation,
										true);

					}else{
						batch.draw(
								texToUse,
								// position
								(int) (gobj.getX() - texToUse.getWidth() / 2),
								(int) (parameters.getWorldHeight() - gobj.getY() - texToUse
										.getHeight() / 2),
										// origin
										(int) texToUse.getWidth() / 2, (int) texToUse
										.getHeight() / 2,
										// width,height
										texToUse.getWidth(), texToUse.getHeight(),
										// scale
										scale, scale,
										// rotation
										rotation,
										// src
										0, 0,
										// width, height
										texToUse.getWidth(), texToUse.getHeight(),
										false, false);
					}

					font.draw(batch, gobj.getBeschriftung(),
							(int) (gobj.getX()),
							(int) (parameters.getWorldHeight() - gobj.getY()));
				}
			}
		}	

		batch.end();		

		shapeRenderer.setProjectionMatrix(cam.combined);
		if(simBorder!=null){
			shapeRenderer.begin(ShapeType.Line);
			shapeRenderer.rect(0,0,simBorder.width,simBorder.height);			
			shapeRenderer.end();
		}		

		//UI
		fpsLabel.setText(Gdx.app.getGraphics().getFramesPerSecond()+"");
		simSpeedLabel.setText(simSpeed+"");
		stage.act(Gdx.graphics.getDeltaTime());
		stage.draw();		
	}

	public void showSimSpeed(long speed) {
		simSpeed = speed;
		if (simSpeed > maxSimSpeed) {
			maxSimSpeed = simSpeed;
		}
	}

	@Override
	public void resize(int width, int height) {
		System.out.println(width + " " + height);
		this.windowWidth = width;
		this.windowHeight = height;
		// batch.getProjectionMatrix().setToOrtho2D(0, 0, width, height);
		//backgroundSprite.setSize(width, height);		
		float aspectRatio = (float) width / (float) height;

		cam = new OrthographicCamera(2f * aspectRatio, 2f);
		cam.position.set(new Vector3(parameters.getWorldWidth()/2,
				(parameters.getWorldHeight() / 2), 0));
		cam.zoom = 2000;
		batch.setProjectionMatrix(cam.combined);
		stage.getViewport().update(width, height, true);
		table.setPosition(windowWidth/2-100, windowHeight/2-50);
	}

	public int getWidth() {
		return windowWidth;
	}

	public int getHeight() {
		return windowHeight;
	}

	public void pause() {
	}

	public void resume() {
	}

	public void dispose() {
	}

	private void handleInput() {
		if (Gdx.input.isKeyPressed(Input.Keys.A)) {
			cam.zoom += 10;
		}
		if (Gdx.input.isKeyPressed(Input.Keys.Q)) {
			cam.zoom -= 10;
		}
		if (Gdx.input.isKeyPressed(Input.Keys.LEFT)) {			
			cam.translate(-8, 0, 0);
		}
		if (Gdx.input.isKeyPressed(Input.Keys.RIGHT)) {			
			cam.translate(8, 0, 0);
		}
		if (Gdx.input.isKeyPressed(Input.Keys.DOWN)) {			
			cam.translate(0, -8, 0);
		}
		if (Gdx.input.isKeyPressed(Input.Keys.UP)) {			
			cam.translate(0, 8, 0);
		}
		if (Gdx.input.isKeyPressed(Input.Keys.W)) {
			cam.rotate(-rotationSpeed, 0, 0, 1);
		}
		if (Gdx.input.isKeyPressed(Input.Keys.E)) {
			cam.rotate(rotationSpeed, 0, 0, 1);
		}
	}
}